home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
-
- thread.c
-
- This module handles sorting subject windows into threads.
-
- Copyright © 1994-1995, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <string.h>
- #include <ctype.h>
- #include <stdio.h>
-
- #include "glob.h"
- #include "thread.h"
- #include "qsort.h"
- #include "newswatcher.h"
- #include "strutil.h"
- #include "memutil.h"
- #include "dialog.h"
- #include "header.h"
- #include "cache.h"
-
-
-
- typedef struct TSortInfo {
- char *canon; /* ptr to canonical subject string */
- TSubject *subject; /* ptr to subject array element */
- short index; /* index in subject array */
- long number; /* article number */
- short partNum; /* part number, or 0x7fff if not a part */
- short numParts; /* number of parts, or 0x7fff if not a part */
- long threadHeadNumber; /* article number of first article in thread */
- short threadOrdinal; /* article ordinal in thread (1,2,3,...) */
- Boolean fromCache; /* true if from cache */
- Boolean potentialPart; /* true if potential part */
- } TSortInfo, *TSortInfoPtr, **TSortInfoHandle;
-
-
-
- static CStr255 gGroupName; /* group name */
- static Handle gStrings; /* handle to subject and author strings */
- static Boolean gParentIsUserGroupList; /* true if parent window is user group list */
-
-
-
- /*----------------------------------------------------------------------------
- IsPartTail
-
- Check string for tail of part indicator.
-
- Entry: x = pointer to string.
- lastChar = required trailing character, or 0 if none.
-
- Exit: function result = true if end of part indicator, in which case:
- *end = pointer to character following end of part indicator.
- *partNum = part number.
- *numParts = number of parts.
- ----------------------------------------------------------------------------*/
-
- static Boolean IsPartTail (char *x, char lastChar, char **end, short *partNum,
- short *numParts)
- {
- short pNum, numP;
-
- while (isLWSP(*x)) x++;
- if (!isdigit(*x)) return false;
- pNum = CrackNum(&x);
- while (isLWSP(*x)) x++;
- if (*x == '/' || *x == '|' || *x == '\\') {
- x++;
- } else if (strncmp(x, "of", 2) == 0) {
- x += 2;
- } else {
- return false;
- }
- while (isLWSP(*x)) x++;
- if (!isdigit(*x)) return false;
- numP = CrackNum(&x);
- if (pNum > numP) return false;
- if (lastChar != 0) {
- while (isLWSP(*x)) x++;
- if (*x != lastChar) return false;
- x++;
- }
- while (isLWSP(*x)) x++;
- *end = x;
- *partNum = pNum;
- *numParts = numP;
- return true;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CheckForPartIndicator
-
- Check subject for part indicator.
-
- Entry: p = pointer to sort info record.
-
- Exit: If the subject contains a part indicator:
- p->partNum = part number.
- p->numParts = number of parts.
- p->potentialPart = true if potential part.
- part indicator substring stripped from p->canon.
- if the part indicator is preceded by non-white space, the
- tail of the subject is also stripped from p->canon.
- number of parts appended to end of p->canon.
-
- Part indicators are defined as follows, using the notation of RFC 822:
-
- part-indicator = "part" part-n-of-m
- / "(" part-n-of-m ")"
- / "[" part-n-of-m "]"
- / "{" part-n-of-m "}"
- / "<" part-n-of-m ">"
-
- part-n-of-m = number "of" number
- / number "/" number
- / number "|" number
- / number "\" number ; first number <= second number
-
- number = 1*DIGIT
-
- If the subject contains a part indicator, any subject prefix in the
- following format is also stripped (for the comp.binaries.ibm.* groups):
-
- "v" number "i" number ":"
-
- A "potential" part is a "part-n-of-m", e.g., a part indicator without the
- word "part" in front or the brackets. A "potential" part is special cased.
- It is considered to be a part if and only if some other matching part is
- also present in the subject list.
- ----------------------------------------------------------------------------*/
-
- static void CheckForPartIndicator (TSortInfoPtr p)
- {
- char *x, *end, *y;
- short partNum, numParts;
- Boolean potentialPart = false;
-
- x = p->canon;
- while (true) {
- x = strpbrk(x, "([{<p0123456789");
- if (x == nil) return;
- if (*x == '(') {
- if (IsPartTail(x+1, ')', &end, &partNum, &numParts)) break;
- } else if (*x == '[') {
- if (IsPartTail(x+1, ']', &end, &partNum, &numParts)) break;
- } else if (*x == '{') {
- if (IsPartTail(x+1, '}', &end, &partNum, &numParts)) break;
- } else if (*x == '<') {
- if (IsPartTail(x+1, '>', &end, &partNum, &numParts)) break;
- } else if (*x == 'p' && strncmp(x, "part", 4) == 0) {
- if (IsPartTail(x+4, 0, &end, &partNum, &numParts)) break;
- } else {
- if (IsPartTail(x, 0, &end, &partNum, &numParts)) {
- potentialPart = true;
- break;
- }
- }
- x++;
- }
- p->partNum = partNum;
- p->numParts = numParts;
- p->potentialPart = potentialPart;
- y = p->canon;
- while (*y >= 0 && !isalnum(*y)) y++;
- if (x == y) {
- BlockMoveData(end, x, strlen(end)+1);
- } else {
- *x = 0;
- }
- x = p->canon;
- if (*x == 'v') {
- x++;
- if (isdigit(*x)) {
- x++;
- while (isdigit(*x)) x++;
- if (*x == 'i') {
- x++;
- if (isdigit(*x)) {
- x++;
- while (isdigit(*x)) x++;
- if (*x == ':') {
- x++;
- while (isLWSP(*x)) x++;
- BlockMoveData(x, p->canon, strlen(x)+1);
- }
- }
- }
- }
- }
- x = p->canon;
- while (*x != 0) x++;
- sprintf(x, "%d", numParts);
- }
-
-
-
- /*----------------------------------------------------------------------------
- InitSortInfo
-
- Initialize sorting info data structures.
-
- Entry: subjectArray = handle to subject array.
- numSubjects = number of subjects.
- strings = handle to subject strings.
-
- Exit: function result = error code.
- *sortInfo = handle to locked sort info array.
- *canonicalStrings = handle to locked canonical subject strings.
- *sortInfoPtrs = handle to locked array of pointers into the
- sort info array.
- subject array locked.
- ----------------------------------------------------------------------------*/
-
- static OSErr InitSortInfo (TSubject **subjectArray, short numSubjects, Handle strings,
- TSortInfoHandle *sortInfo, Handle *canonicalStrings, TSortInfoPtr ***sortInfoPtrs)
- {
- TSortInfoHandle sInfo = nil;
- Handle cStrings = nil;
- TSortInfoPtr **sInfoPtrs = nil;
- OSErr err = noErr;
- TSortInfoPtr p;
- TSubject *q;
- TSortInfoPtr *r;
- short i;
- char *canon, *x;
- long len, reLen;
-
- /* Allocate memory. */
-
- err = MyNewHandle(numSubjects * sizeof(TSortInfo), &sInfo);
- if (err != noErr) goto exit;
- err = MyNewHandle(MyGetHandleSize(strings), &cStrings);
- if (err != noErr) goto exit;
- err = MyNewHandle(numSubjects * sizeof(TSortInfoPtr), &sInfoPtrs);
- if (err != noErr) goto exit;
-
- /* Lock everything. */
-
- MyHLock(sInfo);
- MyHLock(cStrings);
- MyHLock(sInfoPtrs);
- MyHLock(subjectArray);
-
- for (i = 0, p = *sInfo, q = *subjectArray, r = *sInfoPtrs, canon = *cStrings;
- i < numSubjects;
- i++, p++, q++, r++)
- {
- p->canon = canon;
- p->subject = q;
- p->index = i;
- p->number = q->number;
- p->partNum = p->numParts = 0x7fff;
- p->fromCache = q->read;
- p->potentialPart = false;
- *r = p;
- x = *strings + q->subjectOffset;
- len = strlen(x);
- reLen = ParseRe(x, len);
- x += reLen;
- while (*x != 0) {
- if (isalnum(*x) || *x < 0) {
- *canon++ = tolower(*x++);
- } else if (*x == '[' || *x == ']' || *x == '(' || *x == ')' ||
- *x == '/' || *x == '|' || *x == ':') {
- *canon++ = *x++;
- } else {
- x++;
- }
- }
- *canon++ = 0;
- CheckForPartIndicator(p);
- if (reLen > 0) {
- p->partNum = p->numParts = 0x7fff;
- p->potentialPart = false;
- }
- }
-
- *sortInfo = sInfo;
- *canonicalStrings = cStrings;
- *sortInfoPtrs = sInfoPtrs;
- return noErr;
-
- exit:
-
- MyDisposeHandle(sInfo);
- MyDisposeHandle(cStrings);
- MyDisposeHandle(sInfoPtrs);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SortSubjectArrayCompare1
-
- This is the comparison function used to sort the array of pointers to
- TSortInfo records into increasing order by canonical subject.
-
- Entry: p = pointer to pointer to TSortInfo record.
- q = pointer to pointer to TSortInfo record.
-
- Exit: function result = error code.
- *result
- < 0 if first subject < second subject.
- = 0 if first subject == second subject.
- > 0 if first subject > second subject.
-
- The primary sort key is the canonical subject.
-
- The secondary sort key is the part number.
-
- The tertiary sort key is the article number.
- ----------------------------------------------------------------------------*/
-
- static OSErr SortSubjectArrayCompare1 (TSortInfo **p, TSortInfo **q, short *result)
- {
- OSErr err;
- static short counter = 0;
- short r;
-
- if ((++counter & 0x1f) == 0) {
- err = GiveTime(false);
- if (err != noErr) return err;
- counter = 0;
- }
-
- r = strcmp((**p).canon, (**q).canon);
- if (r != 0 ) {
- *result = r;
- } else {
- r = (**p).partNum - (**q).partNum;
- if (r != 0) {
- *result = r;
- } else {
- *result = (**p).number < (**q).number ? -1 : +1;
- }
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SortSubjectArrayCompare2
-
- This is the comparison function used to sort the array of pointers to
- TSortInfo records into final thread order.
-
- Entry: p = pointer to pointer to TSortInfo record.
- q = pointer to pointer to TSortInfo record.
-
- Exit: function result = error code.
- *result
- < 0 if first subject < second subject.
- = 0 if first subject == second subject.
- > 0 if first subject > second subject.
-
- The primary sort key is the thread head article number.
-
- The secondary sort key is the thread ordinal.
- ----------------------------------------------------------------------------*/
-
- static OSErr SortSubjectArrayCompare2 (TSortInfo **p, TSortInfo **q, short *result)
- {
- OSErr err;
- static short counter = 0;
-
- if ((++counter & 0x1f) == 0) {
- err = GiveTime(false);
- if (err != noErr) return err;
- counter = 0;
- }
-
- if ((**p).threadHeadNumber == (**q).threadHeadNumber) {
- *result = (**p).threadOrdinal - (**q).threadOrdinal;
- } else {
- *result = (**p).threadHeadNumber < (**q).threadHeadNumber ? -1 : +1;
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- ProcessThread
-
- Process a thread.
-
- Entry: threadHeadPtr = pointer to first element of TSortInfo array
- for thread.
- threadEndPtr = pointer to element following last element of
- TSortInfo array for thread.
-
- Exit: function result = error code.
-
- On entry, the "fromCache" field of each TSortInfo record is true if the
- article came from the parts cache, false if the article was read
- from the net.
-
- On exit, the following fields are set in each TSubject record:
-
- threadHeadIndex = index in subject array of thread head.
- threadOrdinal = article ordinal within thread (1,2,3,...).
- threadLength = length of thread.
- nextInThread = index in subject array of next article in thread,
- or -1 if last article in thread.
- partNum = part number, or 0x7fff if not a part.
- numParts = number of parts, or 0x7fff if not a part.
- read = false
- incomplete = true if article is part of an incomplete multiple
- part thread.
- inList = true if article should be included in list displayed
- to user (thread contains at least one unread article).
-
- On exit, the following fields are set in each TSortInfo record:
-
- threadHeadNumber = article number of first article in thread.
- threadOrdinal = article ordinal within thread (1,2,3,...).
-
- On exit, the following fields in the TSortInfo records may be
- adjusted:
-
- partNum = part number, or 0x7fff if not a part.
- numParts = number of parts, 0r 0x7fff if not a part.
- ----------------------------------------------------------------------------*/
-
- static OSErr ProcessThread (TSortInfo **threadHeadPtr, TSortInfo **threadEndPtr)
- {
- TSortInfo **s, *x;
- TSubject *q, *prevInList;
- short threadLength, threadHeadNumber, threadHeadIndex, threadOrdinal;
- short numParts, lastPart, numArtsWhichAreParts;
- Boolean inList, incomplete, haveNonPotentialPart, cacheParts;
- Boolean haveNewPart, complete, haveOldPart;
- CStr255 subject, author;
- OSErr err = noErr;
-
- numParts = 0;
- haveNewPart = false;
- haveOldPart = false;
- numArtsWhichAreParts = 0;
- haveNonPotentialPart = false;
- for (s = threadHeadPtr; s < threadEndPtr; s++) {
- x = *s;
- if (x->fromCache && x->numParts < 0x7fff) haveOldPart = true;
- if (!x->fromCache && x->numParts < 0x7fff) haveNewPart = true;
- if (x->numParts < 0x7fff) {
- if (x->numParts > numParts) numParts = x->numParts;
- numArtsWhichAreParts++;
- if (!x->potentialPart) haveNonPotentialPart = true;
- }
- }
-
- if (numArtsWhichAreParts == 0) {
- incomplete = false;
- cacheParts = false;
- complete = false;
- } else if (numArtsWhichAreParts == 1 && !haveNonPotentialPart) {
- incomplete = false;
- cacheParts = true;
- complete = false;
- } else {
- cacheParts = true;
- lastPart = 0;
- for (s = threadHeadPtr; s < threadEndPtr; s++) {
- x = *s;
- if (x->partNum == lastPart+1) lastPart = x->partNum;
- }
- cacheParts = incomplete = lastPart < numParts;
- complete = !incomplete && haveOldPart;
- }
-
- threadHeadNumber = (**threadHeadPtr).number;
- threadOrdinal = 1;
- for (s = threadHeadPtr; s < threadEndPtr; s++) {
- x = *s;
- x->threadHeadNumber = threadHeadNumber;
- x->threadOrdinal = threadOrdinal;
- q = x->subject;
- inList = !x->fromCache ||
- (!incomplete && haveNewPart && x->numParts < 0x7fff);
- if (inList) {
- if (threadOrdinal == 1) threadHeadIndex = x->index;
- if (cacheParts && !x->fromCache && x->partNum < 0x7fff &&
- gParentIsUserGroupList)
- {
- /* This is a new part in an incomplete thread -
- add it to the cache */
- strcpy(subject, *gStrings + q->subjectOffset);
- strcpy(author, *gStrings + q->authorOffset);
- err = AddCachedArticle(gGroupName, q->number, subject, author);
- if (err != noErr) return err;
- } else if (!incomplete && x->fromCache && x->partNum < 0x7fff &&
- gParentIsUserGroupList)
- {
- /* This is part of a thread which is now
- complete - remove it from the cache */
- err = DeleteCachedArticle(gGroupName, q->number);
- if (err != noErr) return err;
- }
- q->threadHeadIndex = threadHeadIndex;
- q->threadOrdinal = threadOrdinal;
- if (numArtsWhichAreParts == 1 && x->potentialPart)
- x->partNum = x->numParts = 0x7fff;
- q->partNum = x->partNum;
- q->numParts = x->numParts;
- q->read = false;
- q->incomplete = incomplete;
- q->complete = complete;
- q->inList = true;
- threadOrdinal++;
- } else {
- q->inList = false;
- }
- }
-
- threadLength = threadOrdinal - 1;
- if (threadLength == 0) return noErr;
-
- prevInList = nil;
- for (s = threadHeadPtr; s < threadEndPtr; s++) {
- x = *s;
- q = x->subject;
- if (q->inList) {
- q->threadLength = threadLength;
- if (prevInList != nil) prevInList->nextInThread = x->index;
- prevInList = q;
- }
- }
- prevInList->nextInThread = -1;
-
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SortArray
-
- Sort the array of TSortInfo pointers into thread order.
-
- Entry: sortInfoPtrs = pointer to array of pointers to TSortInfo records.
- numSubjects = number of subjects.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr SortArray (TSortInfoPtr *sortInfoPtrs, short numSubjects)
- {
- TSortInfo **r, **threadHeadPtr;
- char *threadHeadSubject;
- Boolean newThread;
- short i;
- OSErr err = noErr;
-
- /* Sort the pointer array into increasing order by canonical subject.
- This brings threads together, although the threads are not in the
- right order yet. The article numbers are used as a secondary sort
- key to kep the articles within a thread in the correct order. */
-
- err = FastQSort(sortInfoPtrs, numSubjects, sizeof(TSortInfoPtr),
- (SortCmpFunction)SortSubjectArrayCompare1);
- if (err != noErr) return err;
-
- /* Process the threads. */
-
- for (i = 0, r = sortInfoPtrs; i < numSubjects; i++, r++) {
- newThread = i == 0 || strcmp((**r).canon, threadHeadSubject) != 0;
- if (newThread) {
- if (i != 0) {
- err = ProcessThread(threadHeadPtr, r);
- if (err != noErr) return err;
- }
- threadHeadPtr = r;
- threadHeadSubject = (**r).canon;
- }
- }
- err = ProcessThread(threadHeadPtr, r);
- if (err != noErr) return err;
-
- /* Sort the pointer array into final thread order. The primary
- sort key is threadHeadNumber. The secondary sort key is the
- thread ordinal. This final sort sorts the threads into proper
- chronological order, keeping the articles within the threads
- together. */
-
- return FastQSort(sortInfoPtrs, numSubjects, sizeof(TSortInfoPtr),
- (SortCmpFunction)SortSubjectArrayCompare2);
- }
-
-
-
- /*----------------------------------------------------------------------------
- SplitPartThreads
-
- Split part threads into two threads, the first one containing just the
- parts, and the second one containing just the followups.
-
- Entry: subjectArray = pointer to subject array
- numSubjects = number of subjects.
- ----------------------------------------------------------------------------*/
-
- static void SplitPartThreads (TSubject *subjectArray, short numSubjects)
- {
- short i, j;
- TSubject *s, *t;
- short partsThreadLength, followupsThreadLength;
- short followupsThreadHeadIndex;
- short nextInThread;
-
- for (i = 0, s = subjectArray; i < numSubjects; i++, s++) {
- if (!s->inList) continue;
- if (s->threadOrdinal != 1) continue;
- if (s->threadLength == 1) continue;
- if (s->partNum == 0x7fff) continue;
- t = s;
- partsThreadLength = 0;
- while (t->partNum < 0x7fff && t->nextInThread != -1) {
- partsThreadLength++;
- t = subjectArray + t->nextInThread;
- }
- if (t->partNum < 0x7fff) continue;
- t = s;
- for (j = 1; j <= partsThreadLength; j++) {
- t->threadLength = partsThreadLength;
- nextInThread = t->nextInThread;
- if (j == partsThreadLength) {
- followupsThreadHeadIndex = nextInThread;
- t->nextInThread = -1;
- }
- t = subjectArray + nextInThread;
- }
- followupsThreadLength = t->threadLength - partsThreadLength;
- for (j = 1; j <= followupsThreadLength; j++) {
- t->threadHeadIndex = followupsThreadHeadIndex;
- t->threadOrdinal = j;
- t->threadLength = followupsThreadLength;
- t->incomplete = t->complete = false;
- t = subjectArray + t->nextInThread;
- }
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- SortSubjectArrayCompare3
-
- This is the comparison function used to sort an array of pointers to
- TSubject records into increasing order by article number. It is used
- by the BuildSortArrayByNumber function below.
-
- Entry: p = pointer to pointer to TSubject record.
- q = pointer to pointer to TSubject record.
-
- Exit: function result = error code.
- *result
- < 0 if first article number < second article number.
- > 0 if first article number > second article number.
- ----------------------------------------------------------------------------*/
-
- static OSErr SortSubjectArrayCompare3 (TSubject **p, TSubject **q, short *result)
- {
- OSErr err;
- static short counter = 0;
-
- if ((++counter & 0x1f) == 0) {
- err = GiveTime(false);
- if (err != noErr) return err;
- counter = 0;
- }
-
- *result = (**p).number < (**q).number ? -1 : +1;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- BuildSortByNumberArray
-
- Build the SortByNumber array for the subject window.
-
- Entry: wind = pointer to window record.
-
- Exit: function result = error code.
- (**info).sortByNumber = handle to array of offsets to elements of
- subject array which are in the list, sorted by article
- number.
- ----------------------------------------------------------------------------*/
-
- static OSErr BuildSortByNumberArray (WindowPtr wind)
- {
- TWindow **info;
- TSubject **subjectArray;
- short numSubjects, numSubjectsInList;
- long **sortByNumber = nil;
- OSErr err = noErr;
- short i;
- TSubject *p;
- long *q;
- char state;
-
- info = (TWindow**)GetWRefCon(wind);
- subjectArray = (**info).subjectArray;
- state = MyHGetState(subjectArray);
- numSubjects = (**info).numSubjects;
- numSubjectsInList = (**info).numSubjectsInList;
-
- /* Allocate the array. */
-
- err = MyNewHandle(numSubjectsInList*sizeof(long), &sortByNumber);
- if (err != noErr) goto exit;
-
- /* Initialize the array. During the sort, the array elements are pointers
- into the locked subject array, rather than offsets. */
-
- MyHLock(sortByNumber);
- MyHLock(subjectArray);
-
- for (i = 0, p = *subjectArray, q = *sortByNumber;
- i < numSubjects;
- i++, p++)
- {
- if (p->inList) *q++ = (long)p;
- }
-
- /* Sort the array into increasing order by article number. */
-
- err = FastQSort(*sortByNumber, numSubjectsInList, sizeof(long),
- (SortCmpFunction)SortSubjectArrayCompare3);
- if (err != noErr) goto exit;
-
- /* Convert the array from pointers to offsets. */
-
- for (i = 0, q = *sortByNumber; i < numSubjectsInList; i++, q++)
- *q = (char*)*q - (char*)*subjectArray;
-
- MyHUnlock(sortByNumber);
- MyHSetState(subjectArray, state);
-
- (**info).sortByNumber = sortByNumber;
- return noErr;
-
- exit:
-
- MyDisposeHandle(sortByNumber);
- MyHSetState(subjectArray, state);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- BuildThreads
-
- Build subject window threads.
-
- Entry: wind = pointer to subject window.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr BuildThreads (WindowPtr wind)
- {
- TWindow **info, **parentInfo;
- WindowPtr parent;
- TSubject **subjectArray;
- short numSubjects, numSubjectsInList;
- ListHandle theList;
- TSortInfoHandle sortInfo = nil;
- TSortInfoPtr **sortInfoPtrs = nil;
- Handle canonicalStrings = nil;
- TSubject *q;
- TSortInfoPtr *r;
- short *pCells;
- short *pCellArray;
- short offset;
- short numCells;
- short i;
- OSErr err = noErr;
- char state;
-
- /* Initialize. */
-
- info = (TWindow**)GetWRefCon(wind);
- subjectArray = (**info).subjectArray;
- state = MyHGetState(subjectArray);
- gStrings = (**info).strings;
- theList = (**info).theList;
- strcpy(gGroupName, *gGroupNames + (**info).groupNameOffset);
- parent = (**info).parentWindow;
- if (parent == nil) {
- gParentIsUserGroupList = false;
- } else {
- parentInfo = (TWindow**)GetWRefCon(parent);
- gParentIsUserGroupList = (**parentInfo).groupKind == kUserGroup;
- }
-
- /* Append cached articles to subject array. */
-
- if (gParentIsUserGroupList) {
- err = AppendCachedArticles(wind);
- if (err != noErr) goto exit;
- }
- numSubjects = (**info).numSubjects;
-
- /* Initialize the sort info data structures. */
-
- err = InitSortInfo(subjectArray, numSubjects, gStrings,
- &sortInfo, &canonicalStrings, &sortInfoPtrs);
- if (err != noErr) goto exit;
-
- /* Sort the sortInfoPtrs array into thread order. */
-
- err = SortArray(*sortInfoPtrs, numSubjects);
- if (err != noErr) goto exit;
-
- /* We can and should get rid of the canonicalStrings memory block at this point. */
-
- MyDisposeHandle(canonicalStrings);
- canonicalStrings = nil;
-
- /* Split part threads. */
-
- SplitPartThreads(*subjectArray, numSubjects);
-
- /* Compute the number of subjects in the list and the number
- of cells in the list. */
-
- numSubjectsInList = numSubjects;
- numCells = numSubjects;
- for (i = 0, q = *subjectArray; i < numSubjects; i++, q++) {
- if (!q->inList) {
- numSubjectsInList--;
- numCells--;
- } else if (q->collapsed && q->threadOrdinal != 1) {
- numCells--;
- }
- }
- (**info).numSubjectsInList = numSubjectsInList;
-
- /* Create the List Manager cell list. */
-
- LSetDrawingMode(false, theList);
- LAddRow(numCells, 0, theList);
- err = MySetHandleSize((**theList).cells, 2*numCells);
- if (err != noErr) goto exit;
- pCells = (short*)*((**theList).cells);
- pCellArray = (**theList).cellArray;
- offset = 0;
- for (i = 0, r = *sortInfoPtrs; i < numSubjects; i++, r++) {
- q = (**r).subject;
- if (q->inList) {
- if (!q->collapsed || q->threadOrdinal == 1) {
- *pCellArray++ = offset;
- *pCells++ = (*r)->index;
- offset += 2;
- }
- }
- }
- *pCellArray = offset;
- LSetDrawingMode(true, theList);
-
- /* Dispose the sortInfo and sortInfoPtrs memory blocks. */
-
- MyHSetState(subjectArray, state);
- MyDisposeHandle(sortInfo);
- MyDisposeHandle(sortInfoPtrs);
-
- /* Build the sort by number array. */
-
- return BuildSortByNumberArray(wind);
-
- exit:
-
- MyHSetState(subjectArray, state);
- MyDisposeHandle(sortInfo);
- MyDisposeHandle(canonicalStrings);
- MyDisposeHandle(sortInfoPtrs);
- LDelRow(0, 0, theList);
- LSetDrawingMode(true, theList);
- return err;
- }
-